<template>
<div>
  <el-dialog
    :before-close="beforeClose"
    :title="title"
    :visible="visible">
    <api-table
      v-if="items && items.length>0"
      :items="items"
      :actions="actions"
      :header-ignores="headerIgnores"
      :header-mappings="headerMappings"
      :border="border"
      :stripe="stripe"/>
    <div style="text-align: center;margin-top: 10px">
      <video
        v-show="visibleVideo"
        ref="video"
        :width="canvasWidth"
        :height="canvasHeight"
        autoplay
        playsinline/>
      <canvas
        v-show="visibleCanvas"
        :width="canvasWidth"
        :height="canvasHeight"
        ref="canvas"/>
    </div>
    <div slot="footer" class="dialog-footer" style="text-align: center">
      <el-button
        style="width: 150px"
        @click="cancel">
        取消
      </el-button>

      <el-button
        v-if="visibleOpenCameraBtn"
        type="primary"
        style="width: 150px"
        @click="openCamera"
        :loading="loading">
        {{openCameraBtnName}}
      </el-button>

      <el-button
        v-if="!visibleOpenCameraBtn"
        type="primary"
        style="width: 150px"
        @click="takePhoto"
        :loading="loading">
        {{takePhotoBtnName}}
      </el-button>
    </div>
  </el-dialog>
</div>
</template>

<script>

export default {
  name: "Camera",
  props: {
    title: String,
    canvasWidth : Number,
    canvasHeight: Number,
    openCameraBtnName:{
      type: String,
      default: "打开摄像头"
    },
    takePhotoBtnName:{
      type: String,
      default: "拍照"
    },
    uploadOptionsGen: Function,
    uploadPhotoApi: Function,
    visible: Boolean,
    items: Array,
    emptyItemsErrorMsg: String,
    actions: Array,
    headerIgnores: {
      type: Array,
      default: ()=>[]
    },
    headerMappings: {
      type: Object,
      default: ()=>{}
    },
    border: {
      type: Boolean,
      default: false
    },
    stripe: {
      type: Boolean,
      default: false
    }
  },
  watch:{
    items(val){
      console.log("watchItems",val[0]);
    }
  },
  data(){
    return{
      loading: false,
      visibleOpenCameraBtn: true,
      visibleVideo: false,
      visibleCanvas: false,
      openedCamera: false,
      takeSuccess: false,
    }
  },
  methods:{
    beforeClose(){
      if(this.openedCamera){
        // 关闭摄像头
        this.closeCamera();
      }
      if(this.takeSuccess){
        // 清理canvas
        this.clearCanvas();
      }
      this.visibleVideo = false;
      this.visibleCanvas = false;
      this.visibleOpenCameraBtn = true;
      this.loading = false;
      this.$emit("update:visible", false);
    },
    cancel(){
      this.beforeClose();
    },

    /**
     * 是否有打开摄像头权限
     *    navigator.mediaDevices在目前以下三种情况下可以获取到
     *      1. 地址为localhost:访问时
     *      2. 地址为https: 时
     *      3. 为文件访问file:
     * @returns {*} 是否有权限
     */
    hasOpenCameraPermission(){
      navigator.getUserMedia = navigator.getUserMedia ||
        navigator.webkitGetUserMedia ||
        navigator.mozGetUserMedia ||
        navigator.msGetUserMedia;
      return navigator.getUserMedia;
    },
    openCamera(){
      if(this.emptyItemsErrorMsg && (!this.items || this.items.length===0)){
        this.$message.error(this.emptyItemsErrorMsg);
        return;
      }

      if(this.openedCamera){
        console.log("摄像头已经被打开，无需重复开启");
        return;
      }
      this.loading = true;
      if(!this.hasOpenCameraPermission()){
        this.$message.warning("没有权限打开摄像头");
        this.loading = false;
        return;
      }
      // 从相机捕获视频流
      navigator.mediaDevices.getUserMedia({video:true}).then(stream=>{
        this.visibleCanvas = false;
        this.visibleOpenCameraBtn = false;
        this.openedCamera = true;
        this.visibleVideo = true;
        // 使相机捕获的视频流作为video控件的源对象
        this.$refs.video.srcObject = stream;
      }).finally(()=>{
        this.loading=false;
      });
    },

    closeCamera(){
      if(!this.openedCamera){
        console.log("摄像头没有被打开，无需关闭");
        return;
      }

      let video = this.$refs.video;
      let stream = video.srcObject;
      let tracks = stream.getTracks();
      // 停止从摄像头捕获视频流
      tracks.forEach(track=>track.stop());
      video.srcObject = null;
      this.openedCamera = false;
      this.visibleVideo = false;
    },

    clearCanvas(){
      if(!this.takeSuccess){
        console.log("已经清理过画布，无需重复清理");
        return;
      }

      let canvas = this.$refs.canvas;
      let context = canvas.getContext('2d');
      // 清理canvas残余数据
      context.clearRect(0,0,canvas.width,canvas.height);
      this.takeSuccess = false;
    },

    takePhoto: async function(){
      this.loading=true;
      // 清理上一次残留的canvas
      this.clearCanvas();
      const video = this.$refs.video;
      const canvas = this.$refs.canvas;
      // 将视频帧绘制到画布上
      canvas.width = video.videoWidth;
      canvas.height = video.videoHeight;
      const context = canvas.getContext('2d')
      this.takeSuccess = true;
      context.drawImage(video,0,0,canvas.width - 50,canvas.height - 20);
      const imageDataByBase64 = canvas.toDataURL('image/jpg');
      console.log("拍照成功");
      this.visibleCanvas = true;
      this.visibleVideo = false;

      if(!this.uploadPhotoApi || !this.uploadOptionsGen){
        this.loading = false;
        return;
      }
      let success = false;
      let imageFile = null;
      try{
        imageFile = await this.uploadPhotoByAsync(imageDataByBase64);
        success = true;
      }finally {
        if(success){
          // 关闭对话框
          this.beforeClose();
          this.$emit("onTakePhotoSuccess",imageFile,imageDataByBase64);
        }else {
          // 重新下一轮操作
          this.closeCamera();
          this.visibleOpenCameraBtn = true;
          this.loading = false;
        }
      }
    },

    async uploadPhotoByAsync(imageDataByBase64){
      // 将imageData上传
      let imageFile = this.base64DataToImageFile(imageDataByBase64);
      let formData = this.uploadOptionsGen(imageFile,this.items)
      const res = await this.uploadPhotoApi(formData);
      if(res.code===200){
        this.$message.success("操作成功")
      }else {
        console.error("操作失败",res);
        this.$message.error("操作失败");
      }
      return imageFile;
    },

    /**
     * base64转imageFile
     * @param imageDataByBase64 base64图片
     */
    base64DataToImageFile(imageDataByBase64){
      console.log("base64");
      console.log(imageDataByBase64);
      let arr = imageDataByBase64.split(','),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]),
        n = bstr.length,
        u8arr = new Uint8Array(n);
      while (n--){
        u8arr[n] = bstr.charCodeAt(n);
      }
      // let fileName = await this.getFileName(base64Url);
      // return new File([u8arr], "faceImage."+mime.split('/')[1], {type: mime});
      return new File([u8arr],"faceImage.jpg")
    }

  }
}
</script>
